package hooks

import (
	"io"
)

// blockEOFBuffer acts like bufio.Reader but blocks the final byte and EOF
// until signal from `goon` channel received
type blockEOFBuffer struct {
	rd         io.Reader
	buf        []byte
	trailer    byte // byte cutted from the end of the reader
	swallowded bool

	done  chan struct{}   // underlying reader done
	goon  <-chan struct{} // we can send EOF only if we receive a signal from this channel
	isEOF bool
}

func NewBlockEOFBuffer(goon <-chan struct{}, r io.Reader, bufsize int) *blockEOFBuffer {
	if bufsize <= 0 {
		bufsize = 64 * 1024
	}
	return &blockEOFBuffer{
		rd:   r,
		buf:  make([]byte, bufsize),
		done: make(chan struct{}),
		goon: goon,
	}
}

func (b *blockEOFBuffer) Done() <-chan struct{} {
	return b.done
}

func (b *blockEOFBuffer) Read(p []byte) (n int, err error) {
	if len(p) == 0 {
		return 0, nil
	}

	if b.isEOF {
		return b.onEOF(p)
	}

	maxToRead := min(len(p), len(b.buf))
	n, err = b.rd.Read(b.buf[:maxToRead])
	if err != nil {
		// the underlying reader is done, send signal to subscribers
		close(b.done)
		if err != io.EOF {
			return 0, err
		}

		b.isEOF = true
		if n == 0 {
			// this is the final read
			return b.onEOF(p)
		}
	}

	if n > 0 {
		if b.swallowded {
			p[0] = b.trailer
			b.trailer = b.buf[n-1]
			copy(p[1:], b.buf[:n-1])
		} else {
			b.trailer = b.buf[n-1]
			n = copy(p, b.buf[:n-1])
			b.swallowded = true
		}
	}
	return n, nil
}

func (b *blockEOFBuffer) onEOF(p []byte) (n int, err error) {
	<-b.goon
	if b.swallowded {
		p[0] = b.trailer
		n = 1
	}
	return n, io.EOF
}

func min(x, y int) int {
	if x < y {
		return x
	}
	return y
}

type wrappedWriter struct {
	io.Writer
}
